package com.heu.blood.filter;


import com.heu.blood.common.utils.JwtUtil;
import com.heu.blood.globalExceptionHandler.MyException;
import com.heu.blood.security.entity.LoginUser;
import com.heu.blood.security.exception.SecurityExceptionEnum;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.cache.Cache;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Objects;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private Cache userCache;

    private static final long TIMEOUT = 20 * 60 * 1000; // 1分钟

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if(1 == 1) {
            filterChain.doFilter(request, response);
            return;
        }
        try {
            // 获取token
            String token = request.getHeader("token");
            if (!StringUtils.hasText(token)) {
                // 放行
                filterChain.doFilter(request, response);
                return;
            }
            // 解析token
            String userId;
            try {
                Claims claims = JwtUtil.parseJWT(token);
                userId = claims.getSubject();
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
            // 从redis中获取用户信息
            String key = "login:" + userId;
            // 从loginUserCache对象中获取所需用户信息
            // 通过key确定身份唯一之后，拿出时间进行对比。
            LoginUser loginUser = userCache.get(key, LoginUser.class);
            if (Objects.isNull(loginUser)) {
                throw new MyException(SecurityExceptionEnum.NO_LOGIN);
            }

            // 检查最后活动时间
            Long lastActivityTime = userCache.get("login_time:" + userId, Long.class);
            if (Objects.isNull(lastActivityTime)) {
                throw new MyException(SecurityExceptionEnum.NO_TIME);
            }
            if (lastActivityTime != null && (System.currentTimeMillis() - lastActivityTime) > TIMEOUT) {
                // 超时，删除缓存并抛出异常
                userCache.evict(key);
                userCache.evict("login_time:" + userId);
                throw new MyException(SecurityExceptionEnum.OUT_OF_TIME);
//            throw new RuntimeException("用户登录超时，请重新登录");
            }

            // 未超时，更新最后活动时间
            userCache.put("login_time:" + userId, System.currentTimeMillis());
            // 存入SecurityContextHolder
            // TODO 获取权限信息封装到Authentication中获取权限信息并比对要访问的接口是否有权限
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            filterChain.doFilter(request, response);
        } catch (MyException e) {
            handleException(response, e);
        }
    }

//    统一返回给前端
    private void handleException(HttpServletResponse response, MyException e) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  // 可以根据需要设置不同的状态码
        response.setContentType("application/json; charset=UTF-8");

        // 构造JSON格式的错误响应
        String jsonErrorResponse = String.format("{\"resultCode\": \"%s\", \"resultMsg\": \"%s\"}",
                e.getResultCode(), e.getResultMsg());

        response.getWriter().write(jsonErrorResponse);
    }
}
